//
//  QYAppConnect.swift
//  QYSocketDemo2
//
//  Created by blazer on 2022/10/9.
//

import Foundation

/** 不是所有的回调都是在主线程*/
@objc(QYSocketAPPConnectResponse)
public protocol QYSocketAPPConnectResponse {
    func writed(_ error: Error?)
    func response(_ data: Data, error: Error?)
}

public class QYSocketAPPConnect: NSObject {
    
    @objc public var isConnect: Bool {
        guard let task = urlStreamTask else {
            return false
        }
        return task.state == .running
    }
    
    //default = 1000
    @objc public var maxLenght: Int {
        set {
            mLength = newValue
        }
        get {
            return mLength
        }
    }
    
    //default = 2
   @objc public var readTimeInterval: TimeInterval {
        set {
            rTimeout = newValue
        }
        get {
            return rTimeout
        }
    }
    
    //default = 1
    @objc public var writeTimeInterval: TimeInterval {
        set {
            wTimeout = newValue
        }
        get {
            return wTimeout
        }
    }
    
    @objc public required init(_ host: String, port: Int, delegate: QYSocketAPPConnectResponse? = nil) {
        self.host = host
        self.port = port
        self.delegate = delegate
        configure.allowsCellularAccess = false
        configure.httpShouldUsePipelining = true
        urlSession = URLSession.init(configuration: configure)
        super.init()
    }
    
    @objc public func connect() -> Bool {
        return openTask()
    }
    
    @objc public func disconnect() {
        closeTask()
    }
    
    // 传入1 或 2
    @objc public func send(_ data: Data) throws {
        try write(data)
    }
    
    @objc public func read() throws -> Data {
        return try readData()
    }
    
    private let host: String
    private let port: Int
    private var delegate: QYSocketAPPConnectResponse?
    
    private var maxConnectCount: Int = 0
    private var wTimeout: TimeInterval = 1
    private var rTimeout: TimeInterval = 2
    private var mLength: Int = 1000
    
    private let connectSemaphore: DispatchSemaphore = DispatchSemaphore.init(value: 0)
    private var urlSession: URLSession! = nil
    private var urlStreamTask: URLSessionStreamTask? = nil
    private var configure: URLSessionConfiguration = URLSessionConfiguration.default
}

extension QYSocketAPPConnect {
    private func openTask() -> Bool {
        if urlStreamTask != nil {
            return false
        }
        
        while (maxConnectCount < 4) { // 3次
            if urlStreamTask == nil {
                urlStreamTask = urlSession.streamTask(withHostName: host, port: port)
            }
            if let task = urlStreamTask {
                task.resume()
                if (task.state == .running) {
                    guard let data = "app_connect".data(using: .utf8) else {
                        return false
                    }
                    task.write(data, timeout: writeTimeInterval) { error in
                        if let d = self.delegate {
                            d.writed(error)
                        }
                    }
                    maxConnectCount = 0
                    return true
                }
            }
            
            // 重连 500ms
            Thread.sleep(forTimeInterval: 0.5 * Double(maxConnectCount))
            maxConnectCount += 1;
        }
        maxConnectCount = 0
        return false
    }
    
    private func closeTask() {
        guard let task = urlStreamTask else {
            return
        }
        
        if task.state == .running {
            task.closeWrite()
            task.closeRead()
        }
        task.cancel()
        urlStreamTask = nil
    }
    
    private func write(_ data: Data) throws {
        guard let task = urlStreamTask else {
            throw QYHTTPSocketError.connectFailed
        }
        
        var e: Error? = nil
        task.write(data, timeout: writeTimeInterval) { error in
            e = error
            self.connectSemaphore.signal()
        }
        
        connectSemaphore.wait()
        if e != nil {
            throw e!
        }
    }
    
    private func readData() throws -> Data {
        var data: Data? = nil
        var eof: Bool = false
        var error: Error? = nil

        guard let task = urlStreamTask else {
            throw QYHTTPSocketError.connectFailed
        }
        task.readData(ofMinLength: 0, maxLength: maxLenght, timeout: readTimeInterval) { d, isEOF, e in
            data = d
            eof = isEOF
            error = e
            self.connectSemaphore.signal()
        }
        connectSemaphore.wait()
        if eof {
            throw QYHTTPSocketError.eof
        }

        if error != nil {
            if maxConnectCount > 4 {
                maxConnectCount = 0
                throw QYHTTPSocketError.connectFailed
            }
            maxConnectCount += 1
            throw QYHTTPSocketError.timeout
        }
        if data != nil && data!.count > 0 {
            return data!
        } else {
            if maxConnectCount > 4 {
                maxConnectCount = 0
                throw QYHTTPSocketError.connectFailed
            }
            maxConnectCount += 1
            throw QYHTTPSocketError.noData
        }
    }
}
